#include "stdafx.h"
#include "DBFMemCursorset.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// CDBFMemCursorset
IMPLEMENT_DYNAMIC(CDBFMemCursorset, CDBFRecordset )

CDBFMemCursorset::CDBFMemCursorset(CDBFTableDef* pDBFTable)
{
	m_bDesc = FALSE;

	m_bShowDeletedRec = TRUE;

	m_pTableData = NULL;

	m_pEditRec = NULL;

	m_lLoadRecord = 0;

	m_lpfnIsInclude = NULL;

	m_nCurCursorRecN = -1;

	m_bBOF = TRUE;			
	m_bEOF = TRUE;
}

CDBFMemCursorset::~CDBFMemCursorset()
{
	if (IsOpen())
		Close();
}

BOOL CDBFMemCursorset::Open(LPCTSTR lpszFileName, UINT nOpenFlag)
{
	

	CString strFileName;

	if(lpszFileName == NULL)
		strFileName = GetDefaultDBName();
	else
		strFileName = lpszFileName;

	if (strFileName.GetLength() < 4)
		return FALSE;

	// Re-Opening is invalid.
	if (IsOpen())
	{
		return FALSE;
	}

	m_pTableDef = new CDBFTableDef;
	m_pTableDef->m_bOpen = FALSE;

	m_pTableDef->Open(strFileName, nOpenFlag);

	if(!IsOpen())
		return FALSE;

	m_DBFFields = m_pTableDef->m_DBFFields;
	m_DBFHead = m_pTableDef->m_DBFHead;

	UINT lFileSize = (UINT)m_pTableDef->m_DBFile.GetLength();
	m_pTableData = new DBF_UCHAR[lFileSize];

	m_pTableDef->m_DBFile.Seek(0, CFile::begin);

#if _MSC_VER > 1200
	DBF_CHECK_ERROR(
		m_pTableDef->m_DBFile.Read(m_pTableData, lFileSize) == lFileSize, TG_DBF_ERROR_DBFILE_MEMORYLOAD);
#else
	DBF_CHECK_ERROR(
		m_pTableDef->m_DBFile.ReadHuge(m_pTableData, lFileSize) == lFileSize, TG_DBF_ERROR_DBFILE_MEMORYLOAD);
#endif // _MSC_VER > 1200

	m_lLoadRecord = m_pTableDef->GetRecordCount();

	Requery();
	return TRUE;
}

void CDBFMemCursorset::Close()
{
	CDBFRecordset::Close();

	m_bDesc = FALSE;
	m_bShowDeletedRec = TRUE;

	m_nCurCursorRecN = -1;

	m_bBOF = TRUE;			
	m_bEOF = TRUE;

	if(m_pTableData != NULL) { delete[] m_pTableData; m_pTableData = NULL; }

	m_lLoadRecord = 0;

	if(m_pEditRec != NULL) { delete[] m_pEditRec; m_pEditRec = NULL; }


	m_RecPtr.RemoveAll();
	for(int i = 0; i < 	m_NewRecPtr.GetSize(); i++)
	{
		DBF_UCHAR* pNewRec = (DBF_UCHAR*)m_NewRecPtr[i];
		delete[] pNewRec;
	}
	m_NewRecPtr.RemoveAll();
}

BOOL CDBFMemCursorset::IsBOF() const
{
	assert(IsOpen());
	return (m_nCurCursorRecN == 0);
}

BOOL CDBFMemCursorset::IsEOF() const
{
	assert(IsOpen());
	return (m_nCurCursorRecN >= m_RecPtr.GetSize() - 1);
}

long CDBFMemCursorset::GetRecordCount()
{
	assert(IsOpen());
	return m_RecPtr.GetSize();
}

long CDBFMemCursorset::GetAbsolutePosition()
{
	assert(IsOpen());
	return m_nCurCursorRecN;
}

void CDBFMemCursorset::SetAbsolutePosition(long lPosition)
{
	assert(IsOpen());
	if (lPosition<0 || lPosition>=GetRecordCount())
		return;

	DBF_LONG lCursorRecN = lPosition; 

	// Call Move.
	m_DBFFields.m_pCurRec = GetRecord(lCursorRecN);
	m_nCurCursorRecN = lCursorRecN;
	m_bBOF = m_bEOF = FALSE;

}

void CDBFMemCursorset::MoveNext()
{
	if (IsEOF())
		return;
	m_nEditMode = DbfUtility::noMode;

	DBF_LONG lCursorRecN = m_nCurCursorRecN;
	
	lCursorRecN++;

	if(lCursorRecN < GetRecordCount())
	{
		// Call Move.
		m_DBFFields.m_pCurRec = GetRecord(lCursorRecN);
		m_nCurCursorRecN = lCursorRecN;
		m_bBOF = m_bEOF = FALSE;
	}
	else
	{
		m_bEOF = TRUE;
		m_bBOF = GetRecordCount() > 0; 
	}
}

void CDBFMemCursorset::MovePrev()
{
	if (IsBOF())
		return;

	m_nEditMode = DbfUtility::noMode;

	DBF_LONG lCursorRecN = m_nCurCursorRecN; 

	lCursorRecN--;

	if(lCursorRecN >= 0)
	{
		// Call Move.
		m_DBFFields.m_pCurRec = GetRecord(lCursorRecN);
		m_nCurCursorRecN = lCursorRecN;
		m_bBOF = m_bEOF = FALSE;
	}
	else
	{
		m_bBOF = TRUE;
		m_bEOF = GetRecordCount() > 0; 
	}
}

void CDBFMemCursorset::MoveFirst()
{
	if (GetRecordCount() < 1)
		return;

	m_nEditMode = DbfUtility::noMode;

	DBF_LONG lCursorRecN = 0; 

	// Call Move.
	m_DBFFields.m_pCurRec = GetRecord(lCursorRecN);
	m_nCurCursorRecN = lCursorRecN;
	m_bBOF = m_bEOF = FALSE;

}

void CDBFMemCursorset::MoveLast()
{
	if (GetRecordCount() < 1)
		return;

	m_nEditMode = DbfUtility::noMode;

	DBF_LONG lCursorRecN = GetRecordCount() - 1; 

	// Call Move.
	m_DBFFields.m_pCurRec = GetRecord(lCursorRecN);
	m_nCurCursorRecN = lCursorRecN;
	m_bBOF = m_bEOF = FALSE;

}

void CDBFMemCursorset::Move(long lOffsetRec)
{
	if(!IsOpen())
		return;

	m_nEditMode = DbfUtility::noMode;

	DBF_LONG lCursorRecN = m_nCurCursorRecN + lOffsetRec;
	
	if(lOffsetRec < 0)
	{
		if(lCursorRecN < 0)
		{
			lCursorRecN = 0;
			m_bBOF = TRUE;
		}
		else
			m_bBOF = FALSE;

		m_bEOF = FALSE; 
	}
	else if(lOffsetRec > 0)
	{
		if(lCursorRecN >= GetRecordCount())
		{
			lCursorRecN = GetRecordCount() - 1;
			m_bEOF = TRUE;
		}
		else
			m_bEOF = FALSE;

		m_bBOF = FALSE; 
	}

	// Call Move.
	m_DBFFields.m_pCurRec = GetRecord(lCursorRecN);
	m_nCurCursorRecN = lCursorRecN;
}

void CDBFMemCursorset::CancelUpdate()
{
	assert(IsOpen());
	if (m_nEditMode == DbfUtility::noMode || m_pEditRec == NULL)
		return;
	m_DBFFields.m_pCurRec = GetRecord(m_nCurCursorRecN);

	if(m_pEditRec != NULL)
		delete[] m_pEditRec;

	m_nEditMode = DbfUtility::noMode;
}

void CDBFMemCursorset::AddNew()
{
	assert(IsOpen());
	if (m_nEditMode != DbfUtility::noMode || m_pEditRec != NULL)
		return;

	m_nEditMode = DbfUtility::addnew;

	if(m_pEditRec != NULL)
		delete[] m_pEditRec;

	m_pEditRec = new DBF_CHAR[m_DBFHead.rec_size];

	memset(m_pEditRec, 0, m_DBFHead.rec_size);

	COleVariant varEmpty;
	varEmpty.Clear();

	for(short i = 0; i < m_DBFFields.GetFieldCount(); i++)
	{
		FIELD_REC* pFieldRec = m_DBFFields.GetFieldRec(i);
		TGSetFieldValue(m_pEditRec, pFieldRec, varEmpty);
	}

	m_pEditRec[0] = REC_FLAG_NORMAL;

	m_DBFFields.m_pCurRec = m_pEditRec;
}

void CDBFMemCursorset::Edit()
{
	assert(IsOpen());
	if (m_nEditMode != DbfUtility::noMode || m_nEditMode != NULL)
		return;

	m_nEditMode = DbfUtility::edit;

	if(m_pEditRec != NULL)
		delete[] m_pEditRec;

	m_pEditRec = new DBF_CHAR[m_DBFHead.rec_size];

	memcpy(m_pEditRec, m_DBFFields.m_pCurRec, m_DBFHead.rec_size);

	m_DBFFields.m_pCurRec = m_pEditRec;
}

void CDBFMemCursorset::Update()
{
	assert(IsOpen());
	if (m_nEditMode == DbfUtility::noMode || m_pEditRec == NULL)
		return;

	switch(m_nEditMode)
	{
	case DbfUtility::edit:
		{
			SetRecord(m_nCurCursorRecN, m_pEditRec);
			delete [] m_pEditRec;
			break;
		}
	case DbfUtility::addnew:
		{
			m_DBFHead.last_rec++;
			m_NewRecPtr.Add(m_pEditRec);
			m_RecPtr.Add(m_pEditRec);
			m_nCurCursorRecN = max(0,GetRecordCount() - 1);
			SetRecord(m_nCurCursorRecN, m_pEditRec);
			m_bBOF = m_bEOF = FALSE;
			break;
		}
	}
	
	m_pEditRec = NULL;

	m_DBFFields.m_pCurRec = GetRecord(m_nCurCursorRecN);

	m_nEditMode = DbfUtility::noMode;
}

void CDBFMemCursorset::Delete()
{
	assert(IsOpen());
	if (m_nEditMode != DbfUtility::noMode)
		return;

	m_DBFFields.m_pCurRec[0] = REC_FLAG_DELETED;
	SetRecord(m_nCurCursorRecN, m_DBFFields.m_pCurRec);
}

BOOL CDBFMemCursorset::IsDeleted()
{
	assert(IsOpen());
	return (m_DBFFields.m_pCurRec[0] == REC_FLAG_DELETED);
}

void CDBFMemCursorset::Undelete()
{
	assert(IsOpen());
	if (m_nEditMode != DbfUtility::noMode)
		return;

	m_DBFFields.m_pCurRec[0] = REC_FLAG_NORMAL;
	SetRecord(m_nCurCursorRecN, m_DBFFields.m_pCurRec);
}

void CDBFMemCursorset::Remove()
{
	assert(IsOpen());

	if (m_nEditMode != DbfUtility::noMode)
		return;
	m_DBFHead.last_rec--;
	m_RecPtr.RemoveAt(m_nCurCursorRecN);

	if(m_nCurCursorRecN >= GetRecordCount() && m_nCurCursorRecN > 0)
		m_nCurCursorRecN--;

	m_DBFFields.m_pCurRec = GetRecord(m_nCurCursorRecN);
}

BOOL CDBFMemCursorset::IsOpen() const
{
	
	return (m_pTableDef == NULL ? FALSE : m_pTableDef->m_bOpen);
}

void CDBFMemCursorset::ThrowDBFException(long nTGDBFError)
{
	

	TGThrowDBFException(nTGDBFError);
}

#ifdef _DEBUG
void CDBFMemCursorset::AssertValid() const
{
	CDBFRecordset::AssertValid();
}

void CDBFMemCursorset::Dump(CDumpContext& dc) const
{
	

	CDBFRecordset::Dump(dc);

	dc << "\n";
}
#endif //_DEBUG

DBF_CHAR* CDBFMemCursorset::GetRecord(long lRecNumber)
{
	if (lRecNumber > m_RecPtr.GetSize())
		return NULL;
	return (DBF_CHAR*)m_RecPtr[lRecNumber];
}

void CDBFMemCursorset::GetRecord(long lRecNumber, DBF_CHAR* lpBuffer){}

void CDBFMemCursorset::SetRecord(long lRecNumber, const DBF_CHAR* lpBuffer)
{
	if (lRecNumber > m_RecPtr.GetSize())
		return;
	memcpy(m_RecPtr[lRecNumber],lpBuffer,m_DBFHead.rec_size);
}

void CDBFMemCursorset::SortByField(const char* lpszName, BOOL bDesc /* = FALSE */)
{
	for(short nIndex = 0;
		_stricmp(m_DBFFields.m_pFields[nIndex].field_name, lpszName) != 0 &&
		nIndex <= m_DBFFields.m_nFieldCount;
		nIndex++)
	SortByField(nIndex, bDesc);
}

void CDBFMemCursorset::SortByField(int nIndex, BOOL bDesc /* = FALSE */)
{
	m_nSortedField = nIndex;

	m_bDesc = bDesc;

	if(GetRecordCount() > 1)
	{
		FIELD_REC* pFieldRec = m_DBFFields.GetFieldRec(nIndex);

		switch(pFieldRec->field_type)
		{
		case FLD_TYPE_DOUBLE:
			if(pFieldRec->len_info.num_size.len == sizeof(DBF_DOUBLE))
				break;
		case FLD_TYPE_GENERAL:
		case FLD_TYPE_PICTURE:
			return;
		}

		qsort( 0, m_RecPtr.GetSize() - 1 );

		m_DBFFields.m_pCurRec = GetRecord(m_nCurCursorRecN);
	}
}

int CDBFMemCursorset::compare(const void *elem1, const void *elem2)
{
	// if elem1 < elem2 return TRUE else return FALSE
	COleVariant var1, var2;
	BOOL bResult;

	if(m_bDesc)
	{
		m_DBFFields.m_pCurRec = (DBF_CHAR*)elem1;
		var2 = GetFieldValue(m_nSortedField);
		m_DBFFields.m_pCurRec = (DBF_CHAR*)elem2;
		var1 = GetFieldValue(m_nSortedField);
	}
	else
	{
		m_DBFFields.m_pCurRec = (DBF_CHAR*)elem1;
		var1 = GetFieldValue(m_nSortedField);
		m_DBFFields.m_pCurRec = (DBF_CHAR*)elem2;
		var2 = GetFieldValue(m_nSortedField);
	}
	
	if(var1 == var2)
		bResult = FALSE;
	else
	{
		if(var1.vt == VT_EMPTY && var2.vt != VT_EMPTY)
			bResult = TRUE;
		else if (var2.vt == VT_EMPTY && var1.vt != VT_EMPTY)
			bResult = FALSE;
		else
		{
			switch(var1.vt)
			{
			case VT_BSTR:
				{
					CTDbfString str1 = var1.bstrVal;
					CTDbfString str2 = var2.bstrVal;
					bResult = (str1 < str2);
					break;
				}
			case VT_R8:
				{
					bResult = (var1.dblVal < var2.dblVal);
					break;
				}
			case VT_I4:
				{
					bResult = (var1.lVal < var2.lVal);
					break;
				}
			case VT_CY:
				{
					COleCurrency cur1, cur2;
					cur1.m_cur = var1.cyVal;
					cur2.m_cur = var2.cyVal;
					bResult = (cur1 < cur2);
					break;
				}
			case VT_DATE:
				{
					bResult = (var1.date < var2.date);
					break;
				}
			case VT_BOOL:
				{
					bResult = (var1.boolVal < var2.boolVal);
					break;
				}
			}
		}
	}

	return bResult;
}

void CDBFMemCursorset::qsort(int from, int to)
{
    int i, j;
	void *x, *tmp;

//	int from	= 0;
//	int to		= m_RecPtr.GetSize();

	if( from >= to ) return;

	i = from; j = to;
	x = m_RecPtr[ (i+j) / 2 ];

	do
	{
		while( compare(m_RecPtr[i], x) ) i++ ;	// m_RecPtr[i] < x

		while( compare(x, m_RecPtr[j]) ) j--;	// x < m_RecPtr[j] 

		if( i <= j )
		{
			tmp = m_RecPtr[i]; m_RecPtr[i] = m_RecPtr[j] ; m_RecPtr[j] = tmp;
			i++;  j--;
		}
	} while( i <= j );
            if( (j - from)  <  (to - i) ){
                    qsort( from, j );
                    qsort( i, to   );
            } else {
                    qsort( i,   to );
                    qsort( from, j );
            }
}

void CDBFMemCursorset::SetDelete(BOOL bHideDeleteRec /* = TRUE */)
{
	m_bShowDeletedRec = !bHideDeleteRec;
}

BOOL CDBFMemCursorset::GetDelete()
{
	return !m_bShowDeletedRec;
}

void CDBFMemCursorset::Requery()
{
	assert(IsOpen());
	m_nEditMode = DbfUtility::noMode;

	m_RecPtr.SetSize(m_lLoadRecord + m_NewRecPtr.GetSize());

	long nRec = 0;

	if(m_bShowDeletedRec)
	{
		for(long i = 0; i < m_lLoadRecord; i++)
		{
			m_RecPtr[nRec] =  m_pTableData + m_DBFHead.data_offset + m_DBFHead.rec_size * i;

			if(m_lpfnIsInclude != NULL)
			{
				if((*m_lpfnIsInclude)((DBF_CHAR*)m_RecPtr[nRec],&m_DBFFields))
					nRec++;
			}
			else
				nRec++;
		}

		for(long j = 0; j < m_NewRecPtr.GetSize(); j++)
		{
			m_RecPtr[nRec] = m_NewRecPtr[j];
			if(m_lpfnIsInclude != NULL) 
			{
				if((*m_lpfnIsInclude)((DBF_CHAR*)m_RecPtr[nRec],&m_DBFFields))
					nRec++;
			}
			else
				nRec++;
		}
	}
	else
	{
		for(long i = 0; i < m_lLoadRecord; i++)
		{
			if(REC_FLAG_DELETED != *(DBF_CHAR*)(m_pTableData + m_DBFHead.data_offset + m_DBFHead.rec_size * i))
			{
				m_RecPtr[nRec] =  m_pTableData + m_DBFHead.data_offset + m_DBFHead.rec_size * i;

				if(m_lpfnIsInclude != NULL) 
				{
					if((*m_lpfnIsInclude)((DBF_CHAR*)m_RecPtr[nRec],&m_DBFFields))
						nRec++;
				}
				else
					nRec++;
			}
		}

		for(i = 0; i < m_NewRecPtr.GetSize(); i++)
		{
			if(REC_FLAG_DELETED != *(DBF_CHAR*)(m_NewRecPtr[i]))
			{
				m_RecPtr[nRec] = m_NewRecPtr[i];

				if(m_lpfnIsInclude != NULL) 
				{
					if((*m_lpfnIsInclude)((DBF_CHAR*)m_RecPtr[nRec],&m_DBFFields))
						nRec++;
				}
				else
					nRec++;
			}
		}
	}

	m_RecPtr.SetSize(nRec);
	
	m_DBFHead.last_rec = m_RecPtr.GetSize();

	if(GetRecordCount() > 0)
		MoveFirst();
	else
		m_nCurCursorRecN = 0;
}

BOOL CDBFMemCursorset::Save()
{
	CFile* pDBFile = &m_pTableDef->m_DBFile;
/*	if (!SaveFile.Open(lpszPathName, CFile::modeCreate |
		CFile::modeReadWrite | CFile::shareDenyNone))
	{
//		ReportSaveLoadException(lpszPathName, &fe,
//			TRUE, AFX_IDP_INVALID_FILENAME);
		return FALSE;
	}
*/
	DBF_CHAR* pRec;
	pDBFile->Seek(m_DBFHead.data_offset, CFile::begin);

	for(long i = 0; i < m_lLoadRecord; i++)
	{
		pRec = (DBF_CHAR*)(m_pTableData + m_DBFHead.data_offset + m_DBFHead.rec_size * i);
		pDBFile->Write(pRec, m_DBFHead.rec_size);
	}

	for(long j = 0; j < m_NewRecPtr.GetSize(); j++)
	{
		pRec = (DBF_CHAR*) m_NewRecPtr[j];
		pDBFile->Write(pRec, m_DBFHead.rec_size);
	}

	char eof = REC_EOF;

	pDBFile->Write(&eof, 1);

	COleDateTime curDate = COleDateTime::GetCurrentTime();
	if(m_DBFHead.dbf_id == 0x30) // Visual FoxPro
	{
#if ADS == 15
		m_DBFHead.last_update[0] = atof(curDate.Format(_T("%y")));
#else
		m_DBFHead.last_update[0] = _tstoi(curDate.Format(_T("%y")));	
#endif
		
	}
	else
		m_DBFHead.last_update[0] = curDate.GetYear() - TG_DBF_CENTURY;

	m_DBFHead.last_update[1] = curDate.GetMonth();
	m_DBFHead.last_update[2] = curDate.GetDay();
	m_DBFHead.last_rec = i + j;

	pDBFile->Seek(0, CFile::begin);

	pDBFile->Write(&m_DBFHead, sizeof(DBF_HEAD));

	for(i = 0; i < m_DBFFields.GetFieldCount(); i++)
		pDBFile->Write(m_DBFFields.GetFieldRec(i), sizeof(FIELD_REC));
	char end = 0x0D;
	pDBFile->Write(&end, 1);

	pDBFile->SetLength(m_DBFHead.data_offset + m_DBFHead.last_rec * m_DBFHead.rec_size + 1);

	m_pTableDef->m_DBFHead.last_rec = m_DBFHead.last_rec;

	return TRUE;
}

void CDBFMemCursorset::SetFilter(BOOL (FAR * lpfnIsInclude) (DBF_CHAR*, CDBFFields* pDBFFields))
{
	m_lpfnIsInclude = lpfnIsInclude;
}